package org.fastsyncer.parser.framework;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.lang.StringUtils;
import org.fastsyncer.common.constant.ConnectorConstant;
import org.fastsyncer.common.entity.DatabaseConfig;
import org.fastsyncer.common.entity.LdapConfig;
import org.fastsyncer.common.entity.Mapping;
import org.fastsyncer.common.entity.MappingTask;
import org.fastsyncer.common.entity.WebServiceConfig;
import org.fastsyncer.parser.database.DatabaseParser;
import org.fastsyncer.parser.ldap.LdapParser;
import org.fastsyncer.parser.util.ConvertUtil;
import org.fastsyncer.parser.webservice.WebserviceParser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//单例模式类，通过.getInstance()来创建对象
public class ParserFactory implements Parser {

    // 实现单例模式的方法，通过方法来实现实例化
    // 声明一个私有的静态属性，使只被实例化一次
    private static final ParserFactory instance = new ParserFactory();

    // 声明一个私有的构造方法，使别人不能随便new对象
    // 它只被调用一次，实例存在之后就不再调用了
    private ParserFactory() {
    }

    // 声明一个公共的静态方法，外界通过调用这个方法来得到唯一的实例
    public static ParserFactory getInstance() {
        return instance;
    }

    private static Logger logger = LoggerFactory.getLogger(ParserFactory.class);

    @Override
    public Integer[] parse(MappingTask mappingTask, Integer[] pos, ConcurrentLinkedQueue<JSONObject> reasonQuene) throws Exception {
        // 1.获取数据源数据,[{ID=123, NAME=张三11}, {ID=321, NAME=李四},...]
        List<Map<String, Object>> rows = this.parseSource(mappingTask, pos);

        // 2.数据转换
        List<Map<String, Object>> convertList = ConvertUtil.convertList(mappingTask.getSourceMapping(), mappingTask.getTargetMapping(), rows);

        // 3.同步给目标源
        Integer[] result = this.syncTarget(mappingTask, convertList, pos, reasonQuene);
        return result;
    }

    @Override
    public boolean parse(MappingTask mappingTask, JSONObject row) throws Exception {
        String eventType = row.getString("eventType");
        JSONArray before = row.getJSONArray("before");
        JSONArray after = row.getJSONArray("after");
        JSONArray msg = StringUtils.equals(ConnectorConstant.OPERTION_DELETE, eventType) ? before : after;

        boolean sync = false;
        switch (mappingTask.getTargetMapping().getConnector()) {
        case ConnectorConstant.DB_CONNECTOR:
            // 解析事件类型
            // 新增操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_INSERT, eventType)) {
                sync = DatabaseParser.getInstance().insert(mappingTask, msg);
                break;
            }
            // 修改操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_UPDATE, eventType)) {
                // 目标源主键
                String tpk = parsePrimaryKey(mappingTask.getTargetMapping());
                // 数据源主键
                String spk = parsePrimaryKey(mappingTask.getSourceMapping());
                // 数据源主键值
                String spkVal = parsePrimaryKeyValue(msg, spk);
                sync = DatabaseParser.getInstance().update(mappingTask, tpk, spkVal, msg);
                break;
            }
            // 删除操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_DELETE, eventType)) {
                // 目标源主键
                String tpk = parsePrimaryKey(mappingTask.getTargetMapping());
                // 数据源主键
                String spk = parsePrimaryKey(mappingTask.getSourceMapping());
                // 数据源主键值
                String spkVal = parsePrimaryKeyValue(msg, spk);
                sync = DatabaseParser.getInstance().delete(mappingTask, tpk, spkVal, msg);
                break;
            }
            break;
        case ConnectorConstant.WS_CONNECTOR:
            // 解析事件类型
            // 新增/修改/删除操作
            sync = WebserviceParser.getInstance().send(mappingTask, eventType, msg);
            break;
        case ConnectorConstant.LDAP_CONNECTOR:
            // 解析事件类型
            String spkVal = "";
            if (row.isNull("base")) {
                // 数据源主键
                String spk = parsePrimaryKey(mappingTask.getSourceMapping());
                // 数据源主键值
                spkVal = parsePrimaryKeyValue(msg, spk);
            }else{
                // 只有数据源为LDAP时，该参数base不为空
                spkVal = row.getString("base");
            }

            // 新增操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_INSERT, eventType)) {
                sync = LdapParser.getInstance().insert(mappingTask, spkVal, msg);
                break;
            }
            // 修改操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_UPDATE, eventType)) {
                sync = LdapParser.getInstance().update(mappingTask, spkVal, msg);
                break;
            }
            // 删除操作
            if (StringUtils.equals(ConnectorConstant.OPERTION_DELETE, eventType)) {
                sync = LdapParser.getInstance().delete(mappingTask, spkVal, msg);
                break;
            }
            break;
        default:
            break;
        }
        return sync;
    }

    // 解析数据源
    private List<Map<String, Object>> parseSource(MappingTask mappingTask, Integer[] pos) throws Exception {
        List<Map<String, Object>> rows = null;
        switch (mappingTask.getSourceMapping().getConnector()) {
        case ConnectorConstant.DB_CONNECTOR:
            // 解析数据源Database类型
            Integer startPos = pos[0];
            Integer endPos = pos[1];
            rows = DatabaseParser.getInstance().parseSource(mappingTask, startPos, endPos);
            break;
        case ConnectorConstant.LDAP_CONNECTOR:
            // 解析数据源Ladp类型
            rows = LdapParser.getInstance().parseSource(mappingTask);
            break;
        default:
            break;
        }
        return rows;
    }

    // 迁移至目标源
    private Integer[] syncTarget(MappingTask mappingTask, List<Map<String, Object>> rows, Integer[] pos, ConcurrentLinkedQueue<JSONObject> reasonQuene) throws Exception {
        Integer[] sync = null;
        switch (mappingTask.getTargetMapping().getConnector()) {
        case ConnectorConstant.DB_CONNECTOR:
            // 同步数据源Database类型
            sync = DatabaseParser.getInstance().syncTarget(mappingTask, rows, pos, reasonQuene);
            break;
        case ConnectorConstant.WS_CONNECTOR:
            // 解析数据源webservice类型
            sync = WebserviceParser.getInstance().syncTarget(mappingTask, rows, pos, reasonQuene);
            break;
        case ConnectorConstant.LDAP_CONNECTOR:
            // 同步数据源Ladp类型
            sync = LdapParser.getInstance().syncTarget(mappingTask, rows, pos, reasonQuene);
            break;
        default:
            break;
        }
        return sync;
    }

    /**
     * 获取数据源主键信息
     * @param mapping
     * @return 主键名称
     */
    private String parsePrimaryKey(Mapping mapping) {
        // 判断数据源类型
        String connector = mapping.getConnector();
        String pk = null;
        switch (connector) {
        case ConnectorConstant.DB_CONNECTOR:
            DatabaseConfig dbConfig = (DatabaseConfig) mapping.getConfig();
            pk = dbConfig.getPrimaryKey().toUpperCase();
            break;
        case ConnectorConstant.WS_CONNECTOR:
            WebServiceConfig wsConfig = (WebServiceConfig) mapping.getConfig();
            pk = wsConfig.getPrimaryKey().toUpperCase();
            break;
        case ConnectorConstant.LDAP_CONNECTOR:
            LdapConfig ldapConfig = (LdapConfig) mapping.getConfig();
            pk = ldapConfig.getDn();
            break;
        default:
            break;
        }
        if (null == pk) {
            logger.error("The primary key can not be empty!");
            throw new NullPointerException("主键不能为空!");
        }
        return pk;
    }

    /**
     * 获取数据源主键信息
     * @param row
     * @param pk
     * @return 主键值
     * @throws JSONException
     */
    private String parsePrimaryKeyValue(JSONArray row, String pk) throws JSONException {
        String v = null;
        int length = row.length();
        for (int i = 0; i < length; i++) {
            JSONObject obj = row.getJSONObject(i);
            String sName = obj.getString("name");
            if (sName.equalsIgnoreCase(pk)) {
                // 判断数据类型是字符串还是数组
                Object object = obj.get("value");
                if (object instanceof String) {
                    v = obj.getString("value");
                } else if (object instanceof JSONArray) {
                    // 用“,”拼接值返回
                    v = ConvertUtil.parseValFromJSONArray(obj.getJSONArray("value"));
                }
                break;
            }
        }
        if (null == v) {
            logger.error("The value of primary key can not be empty!");
            throw new NullPointerException("主键值不能为空!");
        }
        return v;
    }

}
